home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / CHFLZ100.ZIP / LZSS32.PAS < prev    next >
Pascal/Delphi Source File  |  1996-09-05  |  21KB  |  869 lines

  1. {$A+,B-,C-,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N-,O+,P-,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}
  2. {
  3.   LZ77 compression for 32-bit Delphi 2: Ported by C.J.Rankin from
  4.   the 16-bit unit LZSSUnit.
  5.  
  6.   Rumour has it that the Pentium Pro cannot handle `partial register
  7.   loads' efficiently; apparently, assigning a value to AL,AH,AX (e.g.)
  8.   and then reading EAX, or assigning AL,AH and reading AX causes the
  9.   pipelines to stall. Call me optimistic/pedantic, but I have tried to
  10.   avoid this where possible.
  11.  
  12.  
  13.   Original unit credits:
  14.    Assembler Programmer: Andy Tam, Pascal Conversion: Douglas Webb,
  15.    Unit Conversion and Dynamic Memory Allocation: Andrew Eigus.
  16.  
  17.    Written by Andrew Eigus (aka: Mr. Byte) of:
  18.    Fidonet: 2:5100/33,
  19.    Internet: aeigus@fgate.castle.riga.lv, aeigus@kristin.cclu.lv.
  20. }
  21. unit LZSS32;
  22.  
  23. interface
  24.  
  25. {#Z+}
  26. { This unit is ready for use with Dj. Murdoch's ScanHelp utility which
  27.   will make a Borland .TPH file for it. }
  28. {#Z-}
  29.  
  30. const Log2TLZSSWord = 2;
  31. {#Z+}
  32. type TLZSSWord = Cardinal;
  33. {#Z-}
  34.  
  35. const
  36.   LZRWBufSize = 32000{8192};  { Read Buffer Size }
  37.  
  38. {#Z+}
  39. const
  40.   N         = 4096;
  41.   F         =   18;
  42.   Threshold =    2;
  43.   Nul       = N*SizeOf(TLZSSWord);
  44.  
  45. var
  46.   InBufPtr:  TLZSSWord = LZRWBufSize;
  47.   InBufSize: TLZSSWord = LZRWBufSize;
  48.   OutBufPtr: TLZSSWord = 0;
  49.  
  50. type
  51. {#X TWriteProc}{#X LZSquash}{#X LZUnsquash}
  52.  
  53.   TReadProc = function(var ReadBuf): TLZSSWord;
  54.   { This is declaration for custom read function. It should read
  55.     #LZRWBufSize# bytes from ReadBuf, returning the number of bytes
  56.     actually read. }
  57.  
  58. {#X TReadProc}{#X LZSquash}{#X LZUnsquash}
  59.  
  60.   TWriteProc = function(var WriteBuf;
  61.                             Count: TLZSSWord): TLZSSWord;
  62.   { This is declaration for custom write function. It should write
  63.     Count bytes into WriteBuf, returning the number of actual bytes
  64.     written. }
  65.  
  66. {#Z+}
  67. type
  68.   PLZRWBuffer = ^TLZRWBuffer;
  69.   TLZRWBuffer = array[0..LZRWBufSize - 1] of Byte; { file buffers }
  70.  
  71.   TLZTextBuf = array[0..N + F - 2] of Byte;
  72.   TLeftMomTree = array[0..N] of TLZSSWord;
  73.   TRightTree = array[0..N + 256] of TLZSSWord;
  74.  
  75.   PBinaryTree = ^TBinaryTree;
  76.   TBinaryTree = record
  77.                   TextBuf: TLZTextBuf;
  78.                   Left:    TLeftMomTree;
  79.                   Right:   TRightTree;
  80.                   Mom:     TLeftMomTree
  81.                 end;
  82.  
  83. const
  84.   LZSSMemRequired = SizeOf(TLZRWBuffer)*2 + SizeOf(TBinaryTree);
  85. {#Z-}
  86.  
  87. function LZInit : boolean;
  88. { This function should be called before any other compression routines
  89.   from this unit - it allocates memory and initializes all internal
  90.   variables required by compression procedures. If allocation fails,
  91.   LZInit returns False, this means that there isn't enough memory for
  92.   compression or decompression process. It returns True if initialization
  93.   was successful. }
  94. {#X LZDone}{#X LZSquash}{#X LZUnsquash}
  95.  
  96. procedure LZSquash(ReadProc: TReadProc; WriteProc: TWriteProc);
  97. { This procedure is used for compression. ReadProc specifies custom
  98.   read function that reads data, and WriteProc specifies custom write
  99.   function that writes compressed data. }
  100. {#X LZUnsquash}{#X LZInit}{#X LZDone}
  101.  
  102. procedure LZUnSquash(ReadProc: TReadProc; WriteProc: TWriteProc);
  103. { This procedure is used for decompression. ReadProc specifies custom
  104.   read function that reads compressed data, and WriteProc specifies
  105.   custom write function that writes decompressed data. }
  106. {#X LZSquash}{#X LZInit}{#X LZDone}
  107.  
  108. procedure LZDone;
  109. { This procedure should be called after you finished compression or
  110.   decompression. It deallocates (frees) all memory allocated by LZInit.
  111.   Note: You should always call LZDone after you finished using compression
  112.   routines from this unit. }
  113. {#X LZInit}{#X LZSquash}{#X LZUnsquash}
  114.  
  115. {#Z+}
  116. var IsLZInitialized : boolean = False;
  117.  
  118. var
  119.   Height, MatchPos, MatchLen, LastLen : TLZSSWord;
  120.   CodeBuf : array[0..16] of Byte;
  121.   LZReadProc:  TReadProc;
  122.   LZWriteProc: TWriteProc;
  123.  
  124. var BinaryTree: PBinaryTree = nil;
  125. var InBufP:     PLZRWBuffer = nil;
  126. var OutBufP:    PLZRWBuffer = nil;
  127. {#Z-}
  128.  
  129. procedure LZEncode;
  130. procedure LZDecode;
  131.  
  132. implementation
  133.  
  134. function LZSS_Read: TLZSSWord;    { Returns # of bytes read }
  135. begin
  136.   Result := LZReadProc(InBufP^)
  137. end; { LZSS_Read }
  138.  
  139. function LZSS_Write: TLZSSWord;  { Returns # of bytes written }
  140. begin
  141.   Result := LZWriteProc(OutBufP^, OutBufPtr)
  142. end; { LZSS_Write }
  143.  
  144. procedure GetC; assembler;
  145. {
  146.   GetC : return a character from the buffer
  147.           RETURN : AL = input char
  148.                    Carry set when EOF
  149. }
  150. asm
  151. {                                          }
  152. { Check for characters in Input Buffer ... }
  153. {                                          }
  154.   MOV EAX, InBufPtr
  155.   CMP EAX, InBufSize
  156.   JB @GetC2
  157. {                                           }
  158. { All chars read. Need to refill buffer ... }
  159. {                                           }
  160.   PUSHAD
  161.   CALL LZSS_Read
  162.   MOV InBufSize, EAX
  163.   TEST EAX, EAX
  164.   POPAD
  165.   JNZ @GetC1
  166. {                                        }
  167. { No bytes read, so EOF: set carry flag. }
  168. {                                        }
  169.   STC
  170.   JMP @Exit
  171. @GetC1:
  172.   XOR EAX, EAX
  173. @GetC2:
  174.   PUSH EBX
  175.   MOV EBX, [OFFSET InBufP]
  176.   MOV EBX, [EBX+EAX]    // Only interested in BL
  177.   INC EAX
  178.   MOV [OFFSET InBufPtr], EAX
  179.   MOV EAX, EBX  // Only interested in AL
  180.   POP EBX
  181.   CLC
  182. @Exit:
  183. end;
  184.  
  185. procedure PutC; assembler;
  186. {
  187.   PutC : put a character into the output buffer
  188.              Entry : AL = output char
  189. }
  190. asm
  191.   PUSH EBX
  192. {                               }
  193. { Store AL in Output buffer ... }
  194. {                               }
  195.   MOV EBX, [OFFSET OutBufPtr]
  196.   PUSH EDI
  197.   MOV EDI, [OFFSET OutBufP]
  198.   MOV [EBX+EDI], AL
  199.   POP EDI
  200. {                                  }
  201. { Check whether buffer is full ... }
  202. {                                  }
  203.   INC EBX
  204.   CMP EBX, LZRWBufSize
  205.   MOV [OFFSET OutBufPtr], EBX
  206.   POP EBX
  207.   JB @Exit
  208. {                                                                          }
  209. { Buffer *IS* full, so flush it (having just set OutBufPtr to LZWRBufSize) }
  210. {                                                                          }
  211.   PUSHAD
  212.   CALL LZSS_Write  // Returns bytes written in EAX ... (not!)
  213.   POPAD
  214.   XOR EAX, EAX
  215.   MOV [OFFSET OutBufPtr], EAX
  216. @Exit:
  217. end;
  218.  
  219. procedure InitTree; assembler;
  220. {
  221.   InitTree : initialize all binary search trees.  There are 256 BST's, one
  222.              for all strings started with a particular character.  The
  223.              parent of tree K is the node N + K + 1 and it has only a
  224.              right child
  225. }
  226. asm
  227.   MOV EDI, [OFFSET BinaryTree]
  228.   ADD DI, OFFSET TBinaryTree.Mom
  229.   MOV ECX, N+1
  230.   MOV EAX, Nul
  231.   REP STOSD
  232. {                                                         }
  233. { Initialise last 256 elements of BinaryTree.Right to Nul }
  234. {                                                         }
  235.   ADD EDI, OFFSET TBinaryTree.Right - OFFSET TBinaryTree.Mom
  236.   MOV CH, (256 SHR 8)    (* i.e. MOV ECX, 256 *)
  237.   REP STOSD
  238. end;
  239.  
  240. {
  241. { These procedures used by Splay:   }
  242. {    EBP      = Addr of Mom         }
  243. {    EAX, ECX = Addr of Left, Right }
  244. {                                   }
  245. procedure ZigZig; assembler;
  246. asm
  247.   MOV EDX, [EAX+ESI]
  248.   MOV [ECX+EBX], EDX
  249.   MOV [EBP+EDX], EBX
  250.   MOV EDX, [EAX+EDI]
  251.   MOV [ECX+ESI], EDX
  252.   MOV [EBP+EDX], ESI
  253.   MOV [EAX+ESI], EBX
  254.   MOV [EAX+EDI], ESI
  255.   MOV [EBP+EBX], ESI
  256.   MOV [EBP+ESI], EDI
  257. end;
  258.  
  259. procedure ZigZag; assembler;
  260. asm
  261.   MOV EDX, [ECX+EDI]
  262.   MOV [EAX+EBX], EDX
  263.   MOV [EBP+EDX], EBX
  264.   MOV EDX, [EAX+EDI]
  265.   MOV [ECX+ESI], EDX
  266.   MOV [EBP+EDX], ESI
  267.   MOV [ECX+EDI], EBX
  268.   MOV [EAX+EDI], ESI
  269.   MOV [EBP+ESI], EDI
  270.   MOV [EBP+EBX], EDI
  271. end;
  272.  
  273. procedure Splay; assembler;
  274. {
  275.   Splay : use splay tree operations to move the node to the 'top' of
  276.            tree.  Note that it will not actual become the root of the tree
  277.            because the root of each tree is a special node.  Instead, it
  278.            will become the right child of this special node.
  279.  
  280.              ENTRY : EDI = the node to be rotated
  281.  
  282.   All registers except EDI are expendable
  283. }
  284. asm
  285. {                                                               }
  286. { Load location of Binary Tree Structure's Mom-array into EBP   }
  287. {                                          Right-array into ECX }
  288. {                                          Left-array into EAX  }
  289.   MOV EAX, [OFFSET BinaryTree]
  290.   LEA EBP, TBinaryTree[EAX].Mom
  291.   LEA ECX, TBinaryTree[EAX].Right
  292.   ADD EAX, OFFSET TBinaryTree.Left
  293. {                           }
  294. { Begin Splay operation ... }
  295. {                           }
  296. @Splay1:
  297.   MOV ESI, [EBP+EDI]
  298.   CMP ESI, Nul
  299.   JA @Exit      // Exit if parent is special
  300.  
  301.   MOV EBX, [EBP+ESI]
  302.   CMP EBX, Nul
  303.   JBE @Splay5  // If nodes's grandparent is NOT special, skip it
  304.  
  305.   CMP EDI, [EAX+ESI] // Check whether current node is left-child
  306.   JNE @Splay2
  307.  
  308.   MOV EDX, [ECX+EDI]  // Perform Left-Zig
  309.   MOV [EAX+ESI], EDX
  310.   MOV [ECX+EDI], ESI
  311.   JMP @Splay3
  312.  
  313. @Splay2:
  314.   MOV EDX, [EAX+EDI]  // Perform Right-Zig
  315.   MOV [ECX+ESI], EDX
  316.   MOV [EAX+EDI], ESI
  317.  
  318. @Splay3:
  319.   MOV [ECX+EBX], EDI
  320.   MOV [EBP+EDX], ESI
  321.   MOV [EBP+ESI], EDI
  322.   MOV [EBP+EDI], EBX
  323.   JMP @Exit
  324.  
  325. @Splay5:
  326.   PUSH DWORD PTR [EBP+EBX]
  327.   CMP EDI, [EAX+ESI]
  328.   JNE @Splay7
  329.   CMP ESI, [EAX+EBX]
  330.   XCHG EAX, ECX       // Swap Left and Right over (temporarily!)
  331.   JNE @Splay6
  332. {                             }
  333. { Perform Left-operations ... }
  334. {                             }
  335.   CALL ZigZig
  336.   XCHG EAX, ECX       // Swap Left and Right back
  337.   JMP @Splay9
  338.  
  339. @Splay6:
  340.   CALL ZigZag
  341.   XCHG EAX, ECX      // Swap Left and Right back
  342.   JMP @Splay9
  343. {                              }
  344. { Perform Right-operations ... }
  345. {                              }
  346. @Splay7:
  347.   CMP ESI, [ECX+EBX]
  348.   JNE @Splay8
  349.   CALL ZigZig
  350.   JMP @Splay9
  351.  
  352. @Splay8:
  353.   CALL ZigZag
  354. {                    }
  355. { Done operations... }
  356. {                    }
  357. @Splay9:
  358.   POP ESI
  359.   CMP ESI, Nul
  360.   JA @Splay10
  361.   CMP EBX, [EAX+ESI]
  362.   JNE @Splay10
  363.   MOV [EAX+ESI], EDI
  364.   JMP @Splay11
  365.  
  366. @Splay10:
  367.   MOV [ECX+ESI], EDI
  368.  
  369. @Splay11:
  370.   MOV [EBP+EDI], ESI
  371.   JMP @Splay1
  372.  
  373. @Exit:
  374. end;
  375.  
  376. procedure InsertNode; assembler;
  377. {
  378.   InsertNode : insert the new node to the corresponding tree.  Note that the
  379.                position of a string in the buffer also served as the node
  380.                number.
  381.              ENTRY : EDI = position in the buffer
  382. }
  383. asm
  384.   PUSHAD
  385.   MOV EBP, [OFFSET BinaryTree]  // EBP now holds address of TextBuf
  386. {                }
  387. { Initialise ... }
  388. {                }
  389.   XOR EDX, EDX
  390.   INC EDX
  391.  
  392.   XOR EAX, EAX
  393.   MOV [OFFSET MatchLen], EAX
  394.   MOV [OFFSET Height], EAX
  395.  
  396.   MOVZX EAX, BYTE PTR [EBP+EDI]
  397.   SHL EDI, Log2TLZSSWord
  398.   LEA ESI, [EAX*(TYPE TLZSSWord)+(N+1)*(TYPE TLZSSWord)]
  399.   MOV EAX, Nul
  400.   MOV [EBP+EDI+OFFSET TBinaryTree.Right], EAX
  401.   MOV [EBP+EDI+OFFSET TBinaryTree.Left], EAX
  402. {                                                 }
  403. { Initialisation complete. Now to insert node ... }
  404. {                                                 }
  405. @Ins1:
  406.   INC Height
  407.   TEST EDX, EDX
  408.   MOV EDX, Nul
  409.   JS @Ins3
  410. {                                         }
  411. { Does this character have a Right-tree ? }
  412. {                                         }
  413.   MOV EAX, [EBP+ESI+OFFSET TBinaryTree.Right]
  414.   CMP EAX, EDX   // EDX = Nul
  415.   JNE @Ins5
  416.   MOV [EBP+ESI+OFFSET TBinaryTree.Right], EDI   // New Tree
  417.   MOV [EBP+EDI+OFFSET TBinaryTree.Mom], ESI
  418.   JMP @Ins11
  419. {                                        }
  420. { Does this character have a Left-tree ? }
  421. {                                        }
  422. @Ins3:
  423.   MOV EAX, [EBP+ESI+OFFSET TBinaryTree.Left]
  424.   CMP EAX, EDX  // EDX = Nul
  425.   JNE @Ins5
  426.   MOV [EBP+ESI+OFFSET TBinaryTree.Left], EDI   // New Tree
  427.   MOV [EBP+EDI+OFFSET TBinaryTree.Mom], ESI
  428.   JMP @Ins11
  429. {                                                               }
  430. { Prepare to scan TextBuf: starting points ESI, EDI; length EBX }
  431. {                                                               }
  432. @Ins5:
  433.   MOV ESI, EAX
  434.   XOR EBX, EBX
  435.   INC EBX
  436.   SHR ESI, Log2TLZSSWord
  437.   ADD ESI, EBP
  438.   SHR EDI, Log2TLZSSWord
  439.   ADD EDI, EBP
  440. @Ins6:
  441.   MOVZX EDX, BYTE PTR [EDI+EBX]
  442.   MOVZX ECX, BYTE PTR [ESI+EBX]
  443.   SUB EDX, ECX
  444.   JNZ @Ins7
  445.   INC EBX
  446.   CMP EBX, F
  447.   JB @Ins6
  448. @Ins7:
  449.   SUB ESI, EBP
  450.   SUB EDI, EBP
  451.   MOV EAX, ESI
  452.   SHL ESI, Log2TLZSSWord
  453.   SHL EDI, Log2TLZSSWord
  454.   CMP EBX, [OFFSET MatchLen]
  455.   JBE @Ins1
  456.   MOV [OFFSET MatchPos], EAX
  457.   MOV [OFFSET MatchLen], EBX
  458.   CMP EBX, F
  459.   JB @Ins1
  460.  
  461. @Ins8:
  462.   LEA ECX, [EBP+OFFSET TBinaryTree.Left]
  463.   LEA EDX, [EBP+OFFSET TBinaryTree.Right]
  464.   ADD EBP, OFFSET TBinaryTree.Mom
  465.  
  466.   MOV EAX, [EBP+ESI]
  467.   MOV [EBP+EDI], EAX
  468.   MOV EAX, [ECX+ESI]
  469.   MOV [ECX+EDI], EAX
  470.   MOV [EBP+EAX], EDI
  471.   MOV EAX, [EDX+ESI]
  472.   MOV [EDX+EDI], EAX
  473.   MOV [EBP+EAX], EDI
  474.   MOV EAX, [EBP+ESI]
  475.  
  476.   CMP ESI, [EDX+EAX]
  477.   JNE @Ins9
  478.  
  479.   MOV [EDX+EAX], EDI
  480.   JMP @Ins10
  481. @Ins9:
  482.   MOV [ECX+EAX], EDI
  483.  
  484. @Ins10:
  485.   MOV DWORD PTR [EBP+ESI], Nul
  486.  
  487. @Ins11:
  488.   CMP Height, 30
  489.   JB @Exit
  490.   CALL Splay
  491.  
  492. @Exit:
  493.   POPAD
  494. end;
  495.  
  496. procedure DeleteNode; assembler;
  497. {
  498.    DeleteNode : delete the node from the tree
  499.  
  500.             ENTRY : ESI = position in the buffer
  501. }
  502. asm
  503.   PUSHAD
  504.   MOV EBP, [OFFSET BinaryTree]
  505.   LEA ECX, [EBP+OFFSET TBinaryTree.Left]
  506.   LEA EDX, [EBP+OFFSET TBinaryTree.Right]
  507.   ADD EBP, OFFSET TBinaryTree.Mom
  508.  
  509.   SHL ESI, Log2TLZSSWord
  510.   MOV EAX, Nul
  511.  
  512.   CMP [EBP+ESI], EAX  { ; if it has no parent then exit }
  513.   JE @Exit
  514.   CMP [EDX+ESI], EAX  { ; does it have a right child ? }
  515.   JNE @HasRight
  516.   MOV EDI, [ECX+ESI]
  517.   JMP @Del3
  518. @HasRight:
  519.   MOV EDI, [ESI+ECX]  { ; does it have a left child ? }
  520.   CMP EDI, EAX
  521.   JNE @HasLeft
  522.   MOV EDI, [ESI+EDX]
  523.   JMP @Del3
  524. @HasLeft:
  525.   MOV EBX, [EDI+EDX]  { ; does it have a right grandchild ? }
  526.   CMP EBX, EAX
  527.   JE @Del2            { ; if no, then skip }
  528. {                                                  }
  529. { Find the rightmost node in the right subtree ... }
  530. {                                                  }
  531. @Del1:
  532.   MOV EDI, EBX
  533.   MOV EBX, [EDX+EDI]
  534.   CMP EBX, EAX
  535.   JNE @Del1
  536. {                                               }
  537. { Move this node as the root of the subtree ... }
  538. {                                               }
  539.   MOV EBX, [EBP+EDI]
  540.   MOV EAX, [ECX+EDI]
  541.   MOV [EDX+EBX], EAX
  542.   MOV [EBP+EAX], EBX
  543.   MOV EBX, [ECX+ESI]
  544.   MOV [ECX+EDI], EBX
  545.   MOV [EBP+EBX], EDI
  546.  
  547. @Del2:
  548.   MOV EBX, [EDX+ESI]
  549.   MOV [EDX+EDI], EBX
  550.   MOV [EBP+EBX], EDI
  551.  
  552. @Del3:
  553.   MOV EBX, [EBP+ESI]
  554.   MOV [EBP+EDI], EBX
  555.   CMP ESI, [EDX+EBX]
  556.   JNE @Del4
  557.   MOV [EDX+EBX], EDI
  558.   JMP @Del5
  559. @Del4:
  560.   MOV [ECX+EBX], EDI
  561. @Del5:
  562.   MOV DWORD PTR [EBP+ESI], Nul
  563.  
  564. @Exit:
  565.   POPAD
  566. end;
  567.  
  568. procedure LZEncode; assembler;
  569. asm
  570. {                                       }
  571. { Need to preserve registers for Delphi }
  572. {                                       }
  573.   PUSHAD
  574. {                }
  575. { Initialise ... }
  576. {                }
  577.   CALL InitTree
  578.   XOR EBX, EBX
  579.   MOV [OFFSET CodeBuf], BL
  580.  
  581.   XOR ESI, ESI
  582.   XOR EDX, EDX
  583.   INC EDX
  584.  
  585.   PUSH EDX  // Temporary variable; accessed as [ESP]
  586.  
  587.   MOV EBP, [OFFSET BinaryTree]
  588.   LEA EDI, [EBP+OFFSET TBinaryTree.TextBuf+N-F]
  589. @Encode2:
  590.   CALL GetC
  591.   JNC @ReadOK
  592.   TEST EBX, EBX
  593.   JZ @Exit
  594.   JMP @Encode4
  595. @ReadOK:
  596.   MOV [EDI+EBX], AL
  597.   INC EBX
  598.   CMP EBX, F
  599.   JB @Encode2
  600.  
  601. @Encode4:
  602.   SUB EDI, EBP
  603.   MOV ECX, EBX
  604.   XOR EBX, EBX
  605.   PUSH EDI
  606.   DEC EDI
  607. @Encode5:
  608.   CALL InsertNode
  609.   INC EBX
  610.   DEC EDI
  611.   CMP EBX, F
  612.   JB @Encode5
  613.   POP EDI
  614.   CALL InsertNode
  615.  
  616. @Encode6:
  617.   MOV EAX, MatchLen
  618.   CMP EAX, ECX
  619.   JBE @Encode7
  620.   MOV EAX, ECX
  621.   MOV MatchLen, EAX
  622. @Encode7:
  623.   CMP EAX, Threshold
  624.   JA @Encode8
  625.   XOR EAX, EAX
  626.   INC EAX
  627.   MOV MatchLen, EAX             // Loads MatchLen with 1
  628.                                 // Only interested in AL
  629.   MOV EAX, [ESP]
  630.   OR [OFFSET CodeBuf], AL
  631.   MOV EAX, [EBP+EDI]            // Only interested in AL
  632.   MOV [EDX+OFFSET CodeBuf], AL
  633.   INC EDX
  634.   JMP @Encode9
  635.  
  636. @Encode8:
  637.   MOV EAX, MatchPos
  638.   MOV [EDX+OFFSET CodeBuf], AL
  639.   INC EDX
  640.   SHL AH, 4
  641.   MOV AL, [OFFSET MatchLen]
  642.   SUB AL, Threshold+1
  643.   ADD AH, AL
  644.   MOV [EDX+OFFSET CodeBuf], AH
  645.   INC EDX
  646.  
  647. @Encode9:
  648.   SHL BYTE PTR [ESP], 1
  649.   JNZ @Encode11
  650.  
  651.   XOR EBX, EBX
  652. @Encode10:
  653.   MOV EAX, [EBX+OFFSET CodeBuf]   // PutC only stores AL
  654.   CALL PutC
  655.   INC EBX
  656.   CMP EBX, EDX
  657.   JB @Encode10
  658.   XOR EDX, EDX
  659.   INC EDX
  660.   MOV [ESP], EDX
  661.   MOV [OFFSET CodeBuf], DH
  662.  
  663. @Encode11:
  664.   MOV EBX, MatchLen
  665.   MOV LastLen, EBX
  666.  
  667.   XOR EBX, EBX
  668. @Encode12:
  669.   CALL GetC
  670.   JC @EncodeY
  671.   CALL DeleteNode
  672.   MOV [EBP+ESI], AL
  673.   CMP ESI, F-1
  674.   JAE @Encode13
  675.   MOV [EBP+ESI+N], AL
  676. @Encode13:
  677.   INC ESI
  678.   AND ESI, N-1
  679.   INC EDI
  680.   AND EDI, N-1
  681.   CALL InsertNode
  682.   INC EBX
  683.   CMP EBX, LastLen
  684.   JB @Encode12
  685.   JMP @Encode16
  686.  
  687. @EncodeX:
  688.   INC EBX
  689.   CALL DeleteNode
  690.   MOV EAX, N-1
  691.   INC ESI
  692.   AND ESI, EAX
  693.   INC EDI
  694.   AND EDI, EAX
  695.   DEC ECX
  696.   JZ @EncodeY
  697.   CALL InsertNode
  698. @EncodeY:
  699.   CMP EBX, LastLen
  700.   JB @EncodeX
  701.  
  702. @Encode16:
  703.   TEST ECX, ECX
  704.   JNZ @Encode6
  705. @Encode17:
  706.   TEST EDX, EDX
  707.   JZ @Exit
  708. {                                                   }
  709. { Write EDX chars from CodeBuf to Output buffer ... }
  710. {                                                   }
  711.   XOR EBX, EBX
  712. @Encode18:
  713.   MOV EAX, [EBX+OFFSET CodeBuf]  // PutC only stores AL
  714.   CALL PutC
  715.   INC EBX
  716.   CMP EBX, EDX
  717.   JB @Encode18
  718. {                                           }
  719. { Restore registers and flush Output buffer }
  720. {                                           }
  721. @Exit:
  722.   POP EDX
  723.   POPAD
  724.   CALL LZSS_Write
  725. end;
  726.  
  727. procedure LZDecode; assembler;
  728. asm
  729. {                                       }
  730. { Need to preserve registers for Delphi }
  731. {                                       }
  732.   PUSHAD
  733. {                }
  734. { Initialise ... }
  735. {                }
  736.   XOR EDX, EDX
  737.   MOV EDI, N-F
  738.   MOV ESI, [OFFSET BinaryTree] // First field in BTree is TextBuf
  739. {                }
  740. { Main loops ... }
  741. {                }
  742. @Decode2:
  743.   SHR EDX, 1
  744.   TEST DH, DH
  745.   JNZ @Decode3
  746.   CALL GetC
  747.   JC @Exit
  748.   MOV DH, $FF
  749.   MOV DL, AL
  750. @Decode3:
  751.   CALL GetC
  752.   JC @Exit
  753. // Two alternatives ... Either:
  754.   TEST DL, 1
  755.   JZ @Decode4
  756. // Or:
  757. //  bt edx, 0
  758. //  jnc @Decode4
  759.   MOV [ESI+EDI], AL
  760.   INC EDI
  761.   AND EDI, N-1
  762.   CALL PutC
  763.   JMP @Decode2
  764. @Decode4:
  765.   MOV EBX, EAX   // Only require MOV BL, AL
  766.   CALL GetC
  767.   JC @Exit
  768.   MOV BH, AL
  769.   SHR BH, 4
  770.   MOVZX ECX, AL
  771.   AND CL, $F
  772.   ADD CL, Threshold
  773.   INC ECX
  774. @Decode5:
  775.   AND EBX, N-1
  776.   MOV EAX, [ESI+EBX]  // Only interested in AL ...
  777.   MOV [ESI+EDI], AL
  778.   INC EDI
  779.   AND EDI, N-1
  780.   CALL PutC
  781.   INC EBX
  782.   DEC ECX
  783.   JNZ @Decode5
  784.   JMP @Decode2
  785. {                                               }
  786. { Restore registers and flush Output buffer ... }
  787. {                                               }
  788. @Exit:
  789.   POPAD
  790.   CALL LZSS_Write
  791. end;
  792.  
  793. function LZInit: boolean;
  794. label
  795.   Abort;
  796. Begin
  797. {
  798.   *Non-interruptable* test for whether this unit is busy...
  799. }
  800.   asm
  801.     BTS DWORD PTR [OFFSET IsLZInitialized], 0 // If IsLZInitialized then goto Abort;
  802.     JC Abort                                  // IsLZInitialized := True;
  803.   end;
  804. {
  805.   Unit WASN'T busy, but it is now ...
  806. }
  807.   try
  808.     New(InBufP);
  809.     New(OutBufP);
  810.     New(BinaryTree)
  811.   except
  812.     LZDone   // Flag unit as `free' again ...
  813.   end;
  814. Abort:
  815.   LZInit := IsLZInitialized
  816. End; { LZInit }
  817.  
  818. Procedure LZDone;
  819. Begin
  820.   if InBufP <> nil then
  821.     Dispose(InBufP);
  822.   if OutBufP <> nil then
  823.     Dispose(OutBufP);
  824.   if BinaryTree <> nil then
  825.     Dispose(BinaryTree);
  826.   IsLZInitialized := False
  827. End; { LZDone }
  828.  
  829. Procedure LZSquash(ReadProc: TReadProc; WriteProc: TWriteProc);
  830. Begin
  831.   if IsLZInitialized then
  832.   begin
  833.     InBufPtr := LZRWBufSize;
  834.     InBufSize := LZRWBufSize;
  835.     OutBufPtr := 0;
  836.     Height := 0;
  837.     MatchPos := 0;
  838.     MatchLen := 0;
  839.     LastLen := 0;
  840.  
  841.     FillChar(BinaryTree^, SizeOf(TBinaryTree), 0);
  842.     FillChar(CodeBuf, SizeOf(CodeBuf), 0);
  843.  
  844.     LZReadProc := ReadProc;
  845.     LZWriteProc := WriteProc;
  846.  
  847.     LZEncode
  848.   end
  849. End; { LZSquash }
  850.  
  851. Procedure LZUnSquash(ReadProc: TReadProc; WriteProc: TWriteProc);
  852. Begin
  853.   if IsLZInitialized then
  854.   begin
  855.     InBufPtr := LZRWBufSize;
  856.     InBufSize := LZRWBufSize;
  857.     OutBufPtr := 0;
  858.     FillChar(BinaryTree^.TextBuf, SizeOf(TLZTextBuf), 0);
  859.  
  860.     LZReadProc := ReadProc;
  861.     LZWriteProc := WriteProc;
  862.  
  863.     LZDecode
  864.   end
  865. End; { LZUnSquash }
  866.  
  867. end.
  868.  
  869.